← Back

JavaScript Fetch Todo List Practice

This exercise uses the JSONPlaceholder test API to practise requesting todo items and displaying them in the browser.

The goal is to send a GET request with fetch(), validate the HTTP response, convert the response body from JSON and render each task with its completion state.

What you are practising

Request flow

Browser
-> fetch(url)
-> API response
-> check response.ok
-> response.json()
-> create todo markup
-> render the list

Working example

The query parameter _limit=10 keeps the example concise. The checkboxes are disabled because they display API data; this lesson does not send updates back to the server.

Loading todo items…

JavaScript

const todoList = document.querySelector(".todo-list");
const status = document.querySelector(".todo-demo-status");

fetch("https://jsonplaceholder.typicode.com/todos?_limit=10")
    .then((response) => {
        if (!response.ok) {
            throw new Error(`HTTP error! Status: ${response.status}`);
        }

        return response.json();
    })
    .then((todos) => {
        todoList.innerHTML = createMarkup(todos);
        status.textContent = `Loaded ${todos.length} todo items.`;
    })
    .catch((error) => {
        status.textContent = `Could not load todo items: ${error.message}`;
    });

function createMarkup(todos) {
    return todos
        .map(({ id, title, completed }) => `
            <li class="todo-list-item" data-id="${id}">
                <input
                    type="checkbox"
                    aria-label="${title}"
                    ${completed ? "checked" : ""}
                    disabled
                >
                <p>${title}</p>
            </li>
        `)
        .join("");
}

How the code works

fetch() starts the request and returns a promise. A fulfilled promise does not automatically mean that the server returned a successful status, so the code checks response.ok.

response.json() reads the response body and returns another promise containing JavaScript data. The next then() receives the todo array.

Inside createMarkup(), destructuring extracts the three properties needed by the interface. map() creates one list-item string for every todo, while join("") combines those strings without commas.

The conditional expression adds checked only when completed is true. The final catch() reports network, response or data-processing errors to the visitor.

Practice tasks

  1. Change _limit=10 to another number and compare the result.
  2. Use filter() to display only completed todo items.
  3. Add the todo id before each title.
  4. Rewrite the request using async and await.
  5. Add a button that reloads the data after an error.

← Back